package org.phalanx.server.ServerCache;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;

/*
BlueWave Inventory Manager
Copyright (C) 2007 Steven Day

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

public class CacheManager {
	private Hashtable<String,Object> cacheReferenceTable;
	private Hashtable<String,Object> cacheManagerTable;
	private Object EMPTY_CACHE_HANDLER = new Object();
	
	private CacheListenerManager clMgr;
	
	public CacheManager(){
		cacheReferenceTable = new Hashtable<String,Object>();
		cacheManagerTable = new Hashtable<String,Object>();
		clMgr = new CacheListenerManager();
	}
	
	public boolean createNewCache(String cName){
		boolean isSuccess = false;
		
		if (!isExists(cName)){
			Cache newCacheObject = new Cache(cName);
			cacheReferenceTable.put(cName, newCacheObject);
			cacheManagerTable.put(cName, EMPTY_CACHE_HANDLER);
			isSuccess = true;
		}
		
		return isSuccess;
	}
	
	public boolean createNewCache(String cName, boolean isPersistenceCache, AbstractCacheHandler cacheHandler, boolean canClear){
		boolean isSuccess = false;
		
		if (!isExists(cName)){
			Cache newCacheObject = new Cache(cName, canClear);
			cacheReferenceTable.put(cName, newCacheObject);
			cacheManagerTable.put(cName,cacheHandler);
			isSuccess = true;
		}
		
		return isSuccess; 
	}
	
	public Cache getCache(String cName){
		Cache c = null;
		
		if (isExists(cName)){
			c = (Cache)cacheReferenceTable.get(cName);
		}
		
		return c;
	}
	
	public boolean deleteCache(String cName){
		boolean isSuccess = false;
		
		if (isExists(cName)){
			cacheReferenceTable.remove(cName);
			cacheManagerTable.remove(cName);
			isSuccess = true;
		}
		
		return isSuccess;
	}
	
	public boolean clearCache(String cName){
		boolean isSuccess = false;
		
		if (isExists(cName)){
			Cache c = getCache(cName);
			if (c != null && c.isClearAllowed()){
				if (c.clearCache()){
					c.unLockClear();
					isSuccess = true;
				}
			}
		}
		
		return isSuccess;
	}
	
	public boolean clearAllCaches(){
		boolean allCleared = false;
		
		cacheReferenceTable.clear();
		cacheManagerTable.clear();
		allCleared = true;
		
		return allCleared;
	}
	
	public boolean isExists(String cName){
		boolean exists = false;
		
		if (cacheReferenceTable.containsKey(cName) &&
				cacheManagerTable.containsKey(cName)){
			exists = true;
		}
		
		return exists;
	}
	
	public AbstractCacheHandler getCacheHandler(String cName){
		AbstractCacheHandler assignedCacheHandler = null;

		if (!cacheManagerTable.get(cName).equals(EMPTY_CACHE_HANDLER)){
			if (isExists(cName)){
				assignedCacheHandler = (AbstractCacheHandler)cacheManagerTable.get(cName);
			}
		}

		return assignedCacheHandler;
	}
	
	public boolean setCacheHandler(String cName, AbstractCacheHandler newCacheHandler){
		boolean isSuccess = false;
		
		if (cacheReferenceTable.containsKey(cName)){
			cacheManagerTable.put(cName, newCacheHandler);
			isSuccess = true;
		}
		
		return isSuccess;
	}
	
	public boolean setElement(String cName, Object key, Cacheable value){
		boolean isSuccess = false;
		
		if (isExists(cName)){
			Cache c = (Cache)cacheReferenceTable.get(cName);
			
			if (key != null){
				if(!cacheManagerTable.get(cName).equals(EMPTY_CACHE_HANDLER)){
					AbstractCacheHandler a = (AbstractCacheHandler)cacheManagerTable.get(cName);
					if (c.getInternalCacheTable().containsKey(key)){
						isSuccess = a.updateElementToDB(c, key, value);
						clMgr.fireCacheEvent(CacheListenerManager.EVENT_UPDATED, cName, value, true);
					}
					else {
						isSuccess = a.addElementToDB(c, key, value);
						if (value.getId() == null)
							value = a.getObjectFromDB(c, key, true);
					}
					c.put(key, value);
					clMgr.fireCacheEvent(CacheListenerManager.EVENT_ADDED, cName, value, true);
				}
				else {
					c.put(key, value);
					isSuccess = true;
					clMgr.fireCacheEvent(CacheListenerManager.EVENT_ADDED, cName, value, false);
				}
			}
		}
		
		return isSuccess;
	}
	
	public boolean setElementNoDB(String cName, Object key, Cacheable value){
		boolean isSuccess = false;
		
		if (isExists(cName)){
			Cache c = (Cache)cacheReferenceTable.get(cName);
			
			if (key != null){
				c.put(key, value);
				isSuccess = true;
				clMgr.fireCacheEvent(CacheListenerManager.EVENT_ADDED, cName, value, false);
			}
		}
		
		return isSuccess;
	}
	
	public Cacheable getElement(String cName, Object key){
		Cacheable element = null;
		
		// This method should usually be invoked if a particular cache
		// doesn't load up all the elements from the database and a search
		// yields no results in the cache. This function does a normal cache 
		// search then upon failure invokes the cacheHandler to try to grab from the DB
		
		Cache c = (Cache)cacheReferenceTable.get(cName);
		
		if (c.getInternalCacheTable().containsKey(key)){
			element = c.get(key);
		}
		else {
			if (cacheManagerTable.get(cName) != EMPTY_CACHE_HANDLER){
				AbstractCacheHandler a = (AbstractCacheHandler)cacheManagerTable.get(cName);
				element = a.getObjectFromDB(c, key, true);
				if (element != null)
					c.put(key, element);
			}
		}
		
		return element;
	}
	
	public Cacheable getElementNoCacheUpdate(String cName, Object key){
		Cacheable element = null;
		
		Cache c = (Cache)cacheReferenceTable.get(cName);
		
		if (c.getInternalCacheTable().containsKey(key)){
			element = c.get(key);
		}
		else {
			if (cacheReferenceTable.get(cName) != EMPTY_CACHE_HANDLER){
				AbstractCacheHandler a = (AbstractCacheHandler)cacheManagerTable.get(cName);
				element = a.getObjectFromDB(c, key, false);
			}
		}
		
		return element;
	}
	
	public void removeElementNoCacheUpdated(String cName, Object key){
		Cache c = (Cache)cacheReferenceTable.get(cName);
		
		Cacheable temp = c.get(key);
		c.delete(key);
		clMgr.fireCacheEvent(CacheListenerManager.EVENT_DELETED, cName, temp, false);
	}
	
	public void removeElement(String cName, Object key){
		Cache c = (Cache)cacheReferenceTable.get(cName);
		Cacheable element = c.get(key);
		
		if (cacheReferenceTable.get(cName) != EMPTY_CACHE_HANDLER){
			AbstractCacheHandler a = (AbstractCacheHandler)cacheManagerTable.get(key);
			a.deleteObjectFromDB(c, key, element);
		}
		
		c.delete(key);
		clMgr.fireCacheEvent(CacheListenerManager.EVENT_DELETED, cName, element, true);
	}
	
	public void populateCache(String cName){
		AbstractCacheHandler cHandler = getCacheHandler(cName);
		if (cHandler != null){
			cHandler.populateCache(getCache(cName));
		}
	}
	
	public ArrayList<Cache> getAllCaches(){
		Iterator<Object> i = cacheReferenceTable.values().iterator();
		ArrayList<Cache> cacheList = new ArrayList<Cache>();
		
		while (i.hasNext()){
			Cache oneCache = (Cache)i.next();
			
			cacheList.add(oneCache);
		}
		
		return cacheList;
	}
	
	public void addCacheListener(String cacheName, CacheListener listener){
		clMgr.addListener(cacheName, listener);
	}
	
	public void removeCacheListener(String cacheName, CacheListener listener){
		clMgr.removeListener(cacheName, listener);
	}
}
